package com.bosssoft.trainee.service.imp;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bosssoft.trainee.bo.UserBO;
import com.bosssoft.trainee.common.exception.Asserts;
import com.bosssoft.trainee.dao.UserDao;
import com.bosssoft.trainee.mbg.dto.UserDTO;
import com.bosssoft.trainee.mbg.mapper.ResourceMapper;
import com.bosssoft.trainee.mbg.mapper.UserMapper;
import com.bosssoft.trainee.mbg.po.Resource;
import com.bosssoft.trainee.mbg.po.User;
import com.bosssoft.trainee.security.util.JwtTokenUtil;
import com.bosssoft.trainee.service.UserCacheService;
import com.bosssoft.trainee.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @Author: wuxin
 * @Date: 2022/6/17
 * @Project rbac
 * @Version 1.0
 * @Description
 */
@Service
@Slf4j
public class UserServiceImp implements UserService {
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private UserDao userDao;
    @Autowired
    private UserCacheService userCacheService;
    @Autowired
    private ResourceMapper resourceMapper;

    /**
     * 用户登录获取token
     *
     * @param username
     * @param password
     */
    @Override
    public String login(String username, String password) {
        String token = null;
        //密码需要客户端加密后传递
        try {
            // 获得用户信息
            UserDetails userDetails = loadUserByUsername(username);
            // 判断用户状态
            if(!passwordEncoder.matches(password,userDetails.getPassword())){
                Asserts.fail("密码不正确");
            }
            if(!userDetails.isEnabled()){
                Asserts.fail("帐号已被禁用");
            }
            // 生成 Token
            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(authentication);
            token = jwtTokenUtil.generateToken(userDetails);
            //可以插入登录历史
        } catch (AuthenticationException e) {
            log.warn("登录异常:{}", e.getMessage());
        }
        return token;
    }

    /**
     * 根据用户名获取后台管理员
     *
     * @param username
     */
    @Override
    public User getByUsername(String username) {
        //从缓存中获取
        User user = userCacheService.getUser(username);
        if(user!=null) return  user;
        User example=new User();
        example.setUsername(username);
        QueryWrapper<User> userWrapper = new QueryWrapper<>();
        userWrapper.lambda().eq(User::getUsername,username);
        return userMapper.selectOne(userWrapper);
    }

    /**
     * 获取用户信息
     *
     * @param username
     */
    @Override
    public UserDetails loadUserByUsername(String username) {
        User user = getByUsername(username);
        if(user!=null){
            List<Resource> resourceList = getResourceList(user.getId());
            return new UserBO(user,resourceList);
        }
        throw new UsernameNotFoundException("用户名或密码错误");
    }

    /**
     * 获取指定用户的可访问资源
     *
     * @param adminId
     */
    @Override
    public List<Resource> getResourceList(int adminId) {
        //TODO-获取该用户能够访问的资源列表
        return resourceMapper.selectList(null);
    }

    /**
     * 刷新token的功能
     *
     * @param oldToken 旧的token
     */
    @Override
    public String refreshToken(String oldToken) {
        return jwtTokenUtil.refreshHeadToken(oldToken);
    }

    /**
     * 根据Id获取用户
     *
     * @param id
     */
    @Override
    public User getById(int id) {
        return userMapper.selectById(id);
    }

    /**
     * 根据example获取用户列表
     *
     * @param pageSize 页大小
     * @param pageNum  页数
     * @param example  样例
     * @return 和example中属性相同的用户列表
     */
    @Override
    public Page<User> getList(int pageSize, int pageNum, UserDTO example) {
        User user = new User();
        BeanUtil.copyProperties(example, user);
        QueryWrapper<User> userqw = new QueryWrapper<>(user);
        return userMapper.selectPage(new Page<>(pageSize, pageSize), userqw);
    }

    /**
     * 新增用户
     *
     * @param userDTO 新增对象
     * @return 受影响的记录数
     */
    @Override
    public int addUser(UserDTO userDTO) {
        if (!checkUsername(userDTO.getUsername())
                && !checkWorkerNum(userDTO.getWorkerNum())) {
            User user = new User();
            BeanUtil.copyProperties(userDTO, user);
            user.setId(null);
            String encodePassword = passwordEncoder.encode(user.getPassword());
            user.setPassword(encodePassword);
            return userMapper.insert(user);
        } else {
            return 0;
        }
    }

    /**
     * 根据Id删除用户
     *
     * @param id 删除对象id
     * @return 受影响的记录数
     */
    @Override
    public int deleteById(int id) {
        return userMapper.deleteById(id);
    }

    /**
     * 更新用户用户信息
     *
     * @param userDTO 更新对象
     * @return 受影响的记录数
     */
    @Override
    public int updateUser(UserDTO userDTO) {
        //TODO-可以补一个后端的username和workerNum的不重复校验
        User user = new User();
        BeanUtil.copyProperties(userDTO, user);
        String encodePassword = passwordEncoder.encode(user.getPassword());
        user.setPassword(encodePassword);

        return userMapper.updateById(user);
    }

    /**
     * 检查工号是否被使用
     *
     * @param workerNum 工号
     * @return true:被使用，false:未被使用
     */
    @Override
    public boolean checkWorkerNum(String workerNum) {
        return userDao.checkWorkerNum(workerNum) > 0;
    }

    /**
     * 检查用户名是否被使用
     *
     * @param username 用户名
     * @return true:被使用，false:未被使用
     */
    @Override
    public boolean checkUsername(String username) {
        return userDao.checkUsername(username) > 0;
    }
}
